package com.wujunshen.chess.engine;

import static com.wujunshen.chess.common.Constants.*;

import com.github.bhlangonijr.chesslib.*;
import com.wujunshen.chess.StartPosition;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;

/**
 * @author wujunshen
 */
@Data
public class Node {
  private Board board;
  private Float value;
  private List<Node> childrenNodes = new ArrayList<>();

  public Node(StartPosition startPosition) {
    this.board = startPosition.getBoard();
  }

  public String getResult() {
    if (board.isMated()) {
      if (board.getSideToMove() == StartPosition.initSide) {
        return LOSS;
      }
      return WIN;
    }

    if (board.isDraw()) {
      return DRAW;
    }

    return null;
  }

  public void evaluate() {
    // evaluate value by children board
    if (!childrenNodes.isEmpty()) {
      List<Float> childrenValue = childrenNodes.stream().map(Node::getValue).toList();

      if (board.getSideToMove() == StartPosition.initSide) {
        value = childrenValue.stream().max(Float::compare).get();
      } else {
        value = childrenValue.stream().min(Float::compare).get();
      }

      return;
    }

    value = 0.0F;

    // piece and square value
    for (int i = 0; i < 64; i++) {
      Square square = Square.squareAt(i);
      Piece piece = board.getPiece(square);

      // square under control value
      if (piece == Piece.NONE) {
        if (board.squareAttackedBy(square, StartPosition.initSide) > 0L) {
          value += 0.2F;
        }

        if (board.squareAttackedBy(square, StartPosition.initSide.flip()) > 0L) {
          value -= 0.2F;
        }
        continue;
      }

      float pieceValue = StartPosition.PIECE_VALUE.get(piece.getPieceType());
      float squareValue =
          StartPosition.SQUARE_VALUE.get(piece.getPieceSide()).get(piece.getPieceType()).get(i);

      if (piece.getPieceSide() == StartPosition.initSide) {
        value += pieceValue + squareValue;
      } else {
        value -= pieceValue + squareValue;
      }

      // piece has root or attacking value
      if (piece.getPieceType() == PieceType.KING) {
        continue;
      }
      if (board.squareAttackedBy(square, StartPosition.initSide) > 0L) {
        value += pieceValue * 0.05F;
      }

      if (board.squareAttackedBy(square, StartPosition.initSide.flip()) > 0L) {
        value -= pieceValue * 0.05F;
      }
    }

    // get value by result.
    String result = getResult();

    if (WIN.equals(result)) {
      value += 10000.0F;
    }

    if (LOSS.equals(result)) {
      value -= 10000.0F;
    }

    if (DRAW.equals(result)) {
      if (value > 0.0F) {
        value -= 10000.0F;
      } else {
        value += 10000.0F;
      }
    }

    // if check + or - value
    if (board.squareAttackedBy(
            board.getKingSquare(StartPosition.initSide.flip()), StartPosition.initSide)
        > 0L) {
      value += 5.00F;
    }

    if (board.squareAttackedBy(
            board.getKingSquare(StartPosition.initSide), StartPosition.initSide.flip())
        > 0L) {
      value -= 5.00F;
    }

    // freedom value
    int currentMoveValue = board.legalMoves().size();
    board.setSideToMove(board.getSideToMove().flip());
    int nextMoveValue = board.legalMoves().size();
    board.setSideToMove(board.getSideToMove().flip());

    if (board.getSideToMove() == StartPosition.initSide) {
      value += (currentMoveValue - nextMoveValue) * 0.2F;
    } else {
      value -= (currentMoveValue - nextMoveValue) * 0.2F;
    }
  }
}
